# -*- coding:utf-8 -*-
import copy
import hashlib
import json
import urllib.parse

from gamebit import *
from chess import *


def flatChessList(lists):
    res = []
    for contain in lists:
        res.append([contain[i][j] for i in range(len(contain)) for j in range(len(contain[i]))])
    return res

def secretPass(res):
    temp = res[0 : 32] # 去掉时间戳
    sha = hashlib.sha1(temp.encode('utf-8'))
    encrypts = sha.hexdigest()
    return encrypts

def checkRequest(dictionary):
    plaintext = dictionary['appId'] + '/' + dictionary['randStr'] + '/' + dictionary['timeStamp']
    secretText = hashlib.md5(plaintext.encode('utf-8')).hexdigest()
    return (secretText == dictionary['safeToken'])

def parseCookie(request): # 使用request拿到cookie
    cookie = request.cookies.to_dict()
    for k in cookie.keys():
        cookie[k] = json.loads(urllib.parse.unquote(cookie[k]))
    return cookie

class ChessAI():
    board = []
    max_depth = 4 # 检索最大深度
    historyVec = ''
    way = [] # 存放最终判断得到的移动路径

    def __init__(self, myBoard, history, side): #参数 类棋盘 AI的行棋方(黑1红0)
        self.board = copy.deepcopy(myBoard) # Board()类对象-深拷贝
        self.historyVec = history
        self.side = int(side)

    def get_result(self, bitStart):
        ai = getNext(self.historyVec)
        if ai is not None and bitStart:
            change = ai['reverse']
            num = list(map(int, ai['step']))
            startX, startY = [const.ROW_MAX - num[1], num[0] if change else const.LINE_MAX - num[0]]
            endX, endY = [const.ROW_MAX - num[3], num[2] if change else const.LINE_MAX - num[2]]
            return [startX, startY, endX, endY]
        val = self.alphaBeta(-const.ai_chess['MAX_VAL'], const.ai_chess['MAX_VAL'], self.max_depth, self.side)
        if not val or val['value'] == -const.ai_chess['MAIN_VAL']:
            self.max_depth = 2
            val = self.alphaBeta(-const.ai_chess['MAX_VAL'], const.ai_chess['MAX_VAL'], self.max_depth, self.side)
        if val and val['value'] != -const.ai_chess['MAIN_VAL']:
            return val['move']
        return None

    def get_score(self): # 获取路径的最终分值，传入参数为最后一次移动的路径
        score = {'b': 0, 'r': 0} # 黑色/红色方总棋力值
        i1 = 0
        while i1 < 10:
            y1 = 0
            while y1 < 9:
                chess = str(self.board.get_board()[i1][y1] or '').split('_')
                if len(chess) == 2:
                    score[chess[0]] += const.detail_val[chess[1]][i1 * 9 + y1]
                y1 += 1
            i1 += 1
        return score['b'] - score['r'] if self.side == 1 else score['r'] - score['b']

    def alphaBeta(self, A, B, depth, side): # Alpha-Beta剪枝算法
        if depth == 0:
            return {'value': self.get_score()} # 局面评价函数
        moves = self.board.get_list(side) # 生成全部走法
        for move in moves:
            movedKey = self.board.get_board()[move[0]][move[1]]
            clearKey = self.board.get_board()[move[2]][move[3]]
            if str(clearKey or '') == 'b_j' or str(clearKey or '') == 'r_j':
                return {'move': move, 'value': 8888}
            else:
                self.board.set_board(movedKey, [move[2], move[3]]) # 走这个走法
                self.board.set_board(None, [move[0], move[1]])
                val = -self.alphaBeta(-B, -A, depth - 1, 1 - side)['value']
                self.board.set_board(movedKey, [move[0], move[1]]) # 还原这个走法
                self.board.set_board(clearKey, [move[2], move[3]])
                if val >= B:
                    return {'move': move,'value': B}
                if val > A:
                    A = val
                    if self.max_depth == depth:
                        self.way = {'move': move, 'value': A}
        if self.max_depth == depth:
            if len(self.way) > 0:
                return self.way
            return False
        return {'move': move, 'value':A}

class Player():
    @staticmethod
    def login(requestForm):
        u = urllib.parse.unquote(requestForm['user'])
        p = urllib.parse.unquote(requestForm['pass'])
        ret = const.db.login(u, secretPass(p))
        if ret is not None:
            ret['succeed'] = True
            return json.dumps(ret, ensure_ascii = False)
        else:
            return json.dumps({'succeed': False}, ensure_ascii=False)

    @staticmethod
    def register(rf):
        n = urllib.parse.unquote(rf['name'])
        p = urllib.parse.unquote(rf['pass'])
        e = urllib.parse.unquote(rf['email'])
        ret = { 'succeed': const.db.register(n, secretPass(p), e) }
        return json.dumps(ret, ensure_ascii = False)

    @staticmethod
    def uploadHead(rf):
        uid = urllib.parse.unquote(rf['uid'])
        url = urllib.parse.unquote(rf['url'])
        ret = const.db.uploadHead(uid, url)
        return json.dumps(ret, ensure_ascii = False)

    @staticmethod
    def getInfo(rf):
        return json.dumps(const.db.getUserInfo(rf['id']), ensure_ascii = False)

class Hall():
    @staticmethod
    def create(requestForm):
        playerAId = requestForm['playerAId'] if 'playerAId' in requestForm else ''
        playerBId = requestForm['playerBId'] if 'playerBId' in requestForm else ''
        ruleAll = urllib.parse.unquote(requestForm['rule'])
        pwd = requestForm['pass'] if 'pass' in requestForm else ''
        remarks = requestForm['desc'] if 'desc' in requestForm else ''
        ret = const.db.create(playerAId, playerBId, ruleAll, pwd, remarks)
        if ret is not None:
            return json.dumps({'succeed': True, 'roomId': ret}, ensure_ascii=False)
        else:
            return json.dumps({'succeed': False}, ensure_ascii=False)

    @staticmethod
    def getAllGame():
        res = const.db.getAllGame()
        return json.dumps(res, ensure_ascii=False)

    @staticmethod
    def leave(playerId, roomId):
        return const.db.leave(playerId, roomId)

    @staticmethod
    def enter(requestForm):
        roomId = requestForm['roomId']
        playerId = requestForm['playerId']
        res = const.db.enter(playerId, roomId)
        return json.dumps(res, ensure_ascii=False)

class Game():
    isStart = False
    history = ['', '']

    def __init__(self):
        self.sk = {'0': [], '1': []}
        self.myRound = 1
        self.board = Board()
        self.p0 = 1
        self.p1 = 0

    def getHistory(self):
        return self.history

    def pushHistory(self, select, click):
        selected = self.formatPos(select)
        clicked = self.formatPos(click)
        his = []
        for i in range(len(self.history)):
            his.append(''.join([str(item) for item in (selected[i] + clicked[i])]))
        self.handleHistory(his) # 添加历史信息

    def formatPos(self, param):
        return [[const.LINE_MAX - param[1], const.ROW_MAX - param[0]], [param[1], const.ROW_MAX - param[0]]]

    def handleHistory(self, current):
        for i in range(len(self.history)):
            self.history[i] += current[i]
        return self.history

    def repentance(self): # 悔棋
        if bool(self.board.history):
            op = self.board.history.pop()
            self.board.board[op[1]][op[2]] = op[0]
            self.board.board[op[3]][op[4]] = op[5]
            if op[0] is not None:
                op[0].setPosition([op[1], op[2]])
            if op[5] is not None:
                op[5].setPosition([op[3], op[4]])
            self.p0 = 1 - self.p0
            self.p1 = 1 - self.p1
        return {'board': self.board.getSimple(), 'p0': self.p0, 'p1': self.p1}

    def overGame(self, roomId, winSide, isDraw): # 房间号,胜方,是否是和棋
        self.isStart = False
        tempDict = const.db.getUserInfo(roomId)
        if 'pa' not in tempDict or 'pb' not in tempDict:
            return False
        tempDict['pa']['session'] += 1
        tempDict['pb']['session'] += 1
        if isDraw == False:
            if winSide == 0:
                tempDict['pa']['win'] += 1
            else:
                tempDict['pb']['win'] += 1
        for st in ['pa', 'pb']:
            if (tempDict[st]['session'] - 1) % 10 - tempDict[st]['session'] % 10 != 0:
                for i in range(len(const.levelDict) - 1):
                    if const.levelDict[i] == tempDict[st]['level']:
                        tempDict[st]['level'] = const.levelDict[i + 1]
        const.db.setUserInfo(tempDict) # 更新两个玩家的数据

    def getPlayer(self):
        return [self.p0, self.p1]

    def reset(self, edition):
        self.board.defaultSet(self.sk, edition)
        self.p0 = 1
        self.p1 = 0
        return self.board

    def getBoard(self):
        return self.board

    def next(self):
        self.p0 = 1 - self.p0
        self.p1 = 1 - self.p1
        needRefresh = False
        if self.p0 == 1: # 此时红方行棋，回合数+1
            self.myRound += 1
            if self.myRound in [5, 10, 15, 20]: # 技能状态改变的回合数
                needRefresh = True
                b = self.getBoard().board
                for row in range(0, const.ROW_MAX + 1):
                    for line in range(0, const.LINE_MAX + 1):
                        if b[row][line] is not None and b[row][line].skillName is not None:
                            sn = b[row][line].skillName
                            if sn.changeActive(self.myRound) == 1:
                                if type(sn) == Evolve or type(sn) == SowDiscord:
                                    b[row][line] = sn.skillEffectRound(b[row][line])
                                else:
                                    sn.skillEffectRound(b[row][line])
        return { 'p0': self.p0, 'p1': self.p1, 'refresh': needRefresh }

class Board():
    result = []
    select = []
    history = [] # 存放的内容为: [[行棋对象, 开始的横, 纵坐标, 结束的的横, 纵坐标, 原来位置的对象], ...]

    def __init__(self):
        self.board = []

    def get_board(self):
        return self.board

    def set_board(self, chess, position):
        row, line = position
        if chess is not None:
            chess.setPosition(position)
        self.board[row][line] = chess

    def get_list(self, side):
        res_list = []
        for row in range(0, const.ROW_MAX + 1):
            for line in range(0, const.LINE_MAX + 1):
                if self.board[row][line] is not None and self.board[row][line].getSide() == side:
                    cal = self.board[row][line].calculate(self.board)
                    for item in cal:
                        res_list.append([row, line, item[0], item[1]])
        return res_list

    # 不能送将的思路就是在走棋后传入1-side，如果True则状态返回
    # 绝杀status的思路暂时没有。。。
    def checkStatus(self, side): # 判断side方是否将对面的军
        loc = []
        r = [0, 1, 2]
        if side == 0:
            r = [7, 8, 9]
        for row in r:
            for line in [3, 4, 5]:
                if type(self.board[row][line]) == King:
                    loc = [row, line] # 先确定对方将的位置
        for outter in self.board:
            for inner in outter:
                if inner is not None and inner.getSide() == side and loc in inner.calculate(self.board):
                    return True # 可以将到
        return False # 不可以将到

    def getSelect(self):
        return self.select

    def click(self, control, position):
        row, line = position # 点击棋子的位置
        if control == 0: # 非行棋者点击事件
            res = []
            if self.board[row][line] is not None:
                res = self.board[row][line].calculate(self.board)
            return res
        needRefresh = False
        if self.board[row][line] is not None and self.select == []: # 选棋子
            self.select = [row, line]
            self.result = self.board[row][line].calculate(self.board)
            print([self.select, '选棋子', self.result])
            return self.result
        elif self.select != [] and [row, line] in self.result:  # 行棋
            x, y = self.select
            status = 0 # 单纯移动
            self.history.append([self.board[x][y], x, y, row, line, self.board[row][line]])
            if self.board[row][line] is not None:
                if type(self.board[row][line]) == King:
                    status = 4 # 游戏结束
                else:
                    status = 1 # 吃子
                    ch = self.board[row][line]
                    if ch is not None and ch.skillName is not None: # 技能状态判断
                        if ch.skillName.skillEffectBekilled(self.board, self.select, [row, line]) is not None:
                            needRefresh = True
            self.board[row][line] = self.board[x][y]
            rName = ''
            if self.board[x][y] is not None:
                rName = self.board[x][y].getName()
                self.board[x][y] = None
                self.board[row][line].setPosition([row, line])
                if self.checkStatus(self.board[row][line].getSide()):
                    status = 2
                self.result = self.select = []
            print(['行棋', { 'name': rName, 'status': status }])
            return {'succeed': True, 'name': rName, 'status': status, 'refresh': needRefresh }
        else:
            self.result = self.select = []
            print('取消选择')
            return self.result

    def show(self):
        for row in range(0, const.ROW_MAX + 1):
            for line in range(0, const.LINE_MAX + 1):
                if self.board[row][line] is not None:
                    print(self.board[row][line].name, end = '\t')
                else:
                    print('\t', end = '')
            print('')

    def defaultSet(self, sk, edition):
        if int(edition) == 2:
            self.board = [
                [Meng(0), Fei(0), Turtle(0), Mandarins(0), King(0), Mandarins(0), Turtle(0), Mao(0), Gun(0)],
                [None, None, None, None, None, None, None, None, None],
                [None, Pao(0), None, None, None, None, None, Demon(0), None],
                [Pawns(0), None, Pawns(0), None, Pawns(0), None, Fire(0), None, Fire(0)],
                [None, None, None, None, None, None, None, None, None],
                [None, None, None, None, None, None, None, None, None],
                [Fire(1), None, Fire(1), None, Pawns(1), None, Pawns(1), None, Pawns(1)],
                [None, Demon(1), None, None, None, None, None, Pao(1), None],
                [None, None, None, None, None, None, None, None, None],
                [Gun(1), Mao(1), Turtle(1), Mandarins(1), King(1), Mandarins(1), Turtle(1), Fei(1), Meng(1)]
            ]
        else:
            self.board = [
                [Rooks(0), Mao(0), Elephants(0), Mandarins(0), King(0), Mandarins(0), Elephants(0), Mao(0), Rooks(0)],
                [None, None, None, None, None, None, None, None, None],
                [None, Pao(0), None, None, None, None, None, Pao(0), None],
                [Pawns(0), None, Pawns(0), None, Pawns(0), None, Pawns(0), None, Pawns(0)],
                [None, None, None, None, None, None, None, None, None],
                [None, None, None, None, None, None, None, None, None],
                [Pawns(1), None, Pawns(1), None, Pawns(1), None, Pawns(1), None, Pawns(1)],
                [None, Pao(1), None, None, None, None, None, Pao(1), None],
                [None, None, None, None, None, None, None, None, None],
                [Rooks(1), Mao(1), Elephants(1), Mandarins(1), King(1), Mandarins(1), Elephants(1), Mao(1), Rooks(1)]
            ]
        skillMap = {'s1': Poison(),'s2': Invisible(),'s3': Resurgence(),'s4': Bomb(),'s5': SowDiscord(),'s6': Evolve()}
        if sk['0'] != [] or sk['1'] != []:
            for s in sk:
                for key in sk[s]:
                    tmp = sk[s][key]
                    self.board[tmp[0]][tmp[1]].setSkill(skillMap[key])
        for row in range(0, const.ROW_MAX + 1):
            for line in range(0, const.LINE_MAX + 1):
                if self.board[row][line] is not None:
                    self.board[row][line].setPosition([row, line])

    def getSimple(self): # 这是为了给前端返回数据
        res = [[], []]
        temp = [[], []]
        for row in range(0, const.ROW_MAX + 1):
            temp = [[],[]]
            for line in range(0, const.LINE_MAX + 1):
                if self.board == []:
                    return json.dumps([], ensure_ascii = False)
                qz = self.board[row][line]
                if qz is None:
                    temp[0].append(None)
                    temp[1].append(None)
                else:
                    sk = None
                    if qz is not None and qz.skillName is not None:
                        sk = qz.skillName.getStr()
                    if qz.getName() == '':
                        temp[qz.getSide()].append({'side': qz.getSide(), 'name': '隐', 'skill': sk })
                        temp[1 - qz.getSide()].append(None)
                    elif sk is not None and type(qz.skillName) == SowDiscord:
                        temp[1 - qz.getSide()].append({'side': qz.getSide(), 'name': qz.getName(), 'skill': sk})
                        temp[qz.getSide()].append({'side': qz.getSide(), 'name': qz.getName()})
                    else:
                        temp[qz.getSide()].append({'side': qz.getSide(), 'name': qz.getName(), 'skill': sk})
                        temp[1 - qz.getSide()].append({'side': qz.getSide(), 'name': qz.getName()})
            res[0].append(temp[0])
            res[1].append(temp[1])
        return res

def test():
    a = Board()
    a.defaultSet({'0': [], '1': []}, 2)
    print(a.click(True, [2,1]))
    print(a.click(True, [2,4]))
    a.show()
    ai = ChessAI(a, '', 1)
    print(ai.get_result())
    return a

